import { promises as fs, existsSync, mkdirSync } from 'node:fs';
import { join, dirname, resolve } from 'node:path';
import { createWriteStream, createReadStream } from 'node:fs';
import type { FileOperationOptions } from '../types/index.js';

export class FileManager {
  async exists(path: string): Promise<boolean> {
    try {
      await fs.access(path);
      return true;
    } catch {
      return false;
    }
  }

  async readFile(path: string, encoding: BufferEncoding = 'utf-8'): Promise<string> {
    return fs.readFile(path, encoding);
  }

  async writeFile(
    path: string,
    content: string,
    options: FileOperationOptions = {}
  ): Promise<void> {
    const { createDirs = true, backup = false, overwrite = true } = options;

    if (!overwrite && await this.exists(path)) {
      throw new Error(`File already exists: ${path}`);
    }

    if (backup && await this.exists(path)) {
      await this.createBackup(path);
    }

    if (createDirs) {
      await this.ensureDir(dirname(path));
    }

    await fs.writeFile(path, content, 'utf-8');
  }

  async copyFile(
    source: string,
    destination: string,
    options: FileOperationOptions = {}
  ): Promise<void> {
    const { createDirs = true, overwrite = true } = options;

    if (!overwrite && await this.exists(destination)) {
      throw new Error(`Destination file already exists: ${destination}`);
    }

    if (createDirs) {
      await this.ensureDir(dirname(destination));
    }

    await fs.copyFile(source, destination);
  }

  async moveFile(source: string, destination: string): Promise<void> {
    await this.ensureDir(dirname(destination));
    await fs.rename(source, destination);
  }

  async deleteFile(path: string): Promise<void> {
    if (await this.exists(path)) {
      await fs.unlink(path);
    }
  }

  async ensureDir(path: string): Promise<void> {
    try {
      await fs.mkdir(path, { recursive: true });
    } catch (error: any) {
      if (error.code !== 'EEXIST') {
        throw error;
      }
    }
  }

  async readDir(path: string): Promise<string[]> {
    return fs.readdir(path);
  }

  async isDirectory(path: string): Promise<boolean> {
    try {
      const stats = await fs.stat(path);
      return stats.isDirectory();
    } catch {
      return false;
    }
  }

  async isFile(path: string): Promise<boolean> {
    try {
      const stats = await fs.stat(path);
      return stats.isFile();
    } catch {
      return false;
    }
  }

  async getFileSize(path: string): Promise<number> {
    const stats = await fs.stat(path);
    return stats.size;
  }

  async createBackup(path: string): Promise<string> {
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    const backupPath = `${path}.backup.${timestamp}`;
    await this.copyFile(path, backupPath);
    return backupPath;
  }

  resolvePath(...paths: string[]): string {
    return resolve(...paths);
  }

  joinPath(...paths: string[]): string {
    return join(...paths);
  }

  async findFiles(
    directory: string,
    pattern: RegExp,
    recursive: boolean = true
  ): Promise<string[]> {
    const results: string[] = [];
    const items = await this.readDir(directory);

    for (const item of items) {
      const fullPath = join(directory, item);
      const isDir = await this.isDirectory(fullPath);

      if (isDir && recursive) {
        const subResults = await this.findFiles(fullPath, pattern, recursive);
        results.push(...subResults);
      } else if (!isDir && pattern.test(item)) {
        results.push(fullPath);
      }
    }

    return results;
  }

  async createTempFile(prefix: string = 'temp', suffix: string = '.tmp'): Promise<string> {
    const tempDir = process.env.TMPDIR || process.env.TMP || '/tmp';
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(2);
    const tempPath = join(tempDir, `${prefix}-${timestamp}-${random}${suffix}`);
    
    await this.writeFile(tempPath, '');
    return tempPath;
  }
}

export const createFileManager = (): FileManager => {
  return new FileManager();
};
